/*
 * @Author: xiaosihan 
 * @Date: 2024-01-07 18:36:18 
 * @Last Modified by: xiaosihan
 * @Last Modified time: 2024-03-07 16:07:08
 */


/*
 * @Author: xiaosihan 
 * @Date: 2023-12-10 04:40:28 
 * @Last Modified by: xiaosihan
 * @Last Modified time: 2024-01-07 18:37:05
 */

import editor3DStore from "@views/editor3D/editor3DStore";
import { uniq } from "lodash";
import { autorun, toJS } from "mobx";
import { DoubleSide, Euler, Group, InstancedBufferAttribute, InstancedMesh, Matrix4, Mesh, MeshBasicMaterial, MeshLambertMaterial, Quaternion, Vector3 } from "three";
import threeUtils from "three-base/threeUtils";
import MeshBase from "../MeshBase/MeshBase";
import InstancedGroupMesh from "../InstancedGroupMesh";

import { degToRad, generateUUID } from "three/src/math/MathUtils";
import editor3D from "@views/editor3D/editor3D";
import ModelEventBox from "../ModelGroup/ModelEventBox";

// 楼层模型组
class FloorMeshGroup extends Group {
    constructor() {
        super();
    }

    static _position: Vector3 = new Vector3();
    static _scale: Vector3 = new Vector3(1, 1, 1);
    static _rotation: Euler = new Euler();
    static _Quaternion: Quaternion = new Quaternion();
    static _Matrix4: Matrix4 = new Matrix4();

    //模型点击盒子
    modelEventBox = (() => {
        const modelEventBox = new ModelEventBox();

        //three事件
        modelEventBox.userData = {
            cursor: "pointer",
            enableEvent: true,
        };

        // 左键点击确认添加
        modelEventBox.addEventListener("leftclick", (e: any) => {
            const { state } = editor3DStore;
            if (state === "addFloor") {
                editor3DStore.cancelAdd();
            }
        });

        // 右键点击取消添加
        modelEventBox.addEventListener("rightclick", (e: any) => {
            const { state, addFloorid } = editor3DStore;
            if (state === "addFloor" && addFloorid) {
                editor3DStore.deleteFloorById(addFloorid);
            }
            editor3DStore.cancelAdd();
        });

        // 移动添加预览模型
        modelEventBox.addEventListener("mousemove", (e: any) => {
            const { state, addFloorid } = editor3DStore;

            if (state === "addFloor") {

                const matrix = new Matrix4();
                const position = new Vector3();
                modelEventBox.getMatrixAt(e.instanceId, matrix);
                position.setFromMatrixPosition(matrix);
                const scale = matrix.elements[5]; // 获取y缩放值
                position.y += scale;

                editor3DStore.setFloorDataById(addFloorid, {
                    positionX: position.x,  // x坐标
                    positionY: position.y,  // y坐标
                    positionZ: position.z,  // z坐标
                })
            }
        });

        this.add(modelEventBox);
        return modelEventBox;
    })();

    //实例模型组
    group = (() => {
        const group = new class InstanceGroup extends Group {
            constructor() {
                super();
            }
            //实例模型组
            declare children: Array<InstancedGroupMesh>;
        }();

        this.add(group);
        return group;
    })();

    dispose = autorun(async () => {
        const { activeFloorId, inFloorId, addFloorid } = editor3DStore;
        //不渲染当前选中的模型
        const floorDatas = toJS(editor3DStore.floorDatas);

        //按照url 分组
        const urls = uniq(floorDatas.map(d => d.url));

        //显示有效楼层
        this.group.children.map(f => {
            f.visible = urls.includes(f.url);
        });

        // 遍历 url
        for (let url of urls) {
            const modelDatasWithUrl = floorDatas.filter(d => d.url === url);
            // 按照 url来获取 InstanceMeshGroup 对象
            const instanceMeshGroup = await this.getInstanceMeshGroupByUrl(url);

            for (let i = 0; i < modelDatasWithUrl.length; i++) {

                // 如果实例数量不对 就需要创建新的实例矩阵
                if (modelDatasWithUrl.length !== instanceMeshGroup.count) {
                    instanceMeshGroup.count = modelDatasWithUrl.length;
                    instanceMeshGroup.children.map((instanceMesh: InstancedMesh) => {
                        instanceMesh.instanceMatrix = new InstancedBufferAttribute(new Float32Array(modelDatasWithUrl.length * 16), 16);
                        instanceMesh.count = instanceMeshGroup.count;
                    });
                }

                // 设置矩阵
                FloorMeshGroup._position.set(modelDatasWithUrl[i].positionX, modelDatasWithUrl[i].positionY, modelDatasWithUrl[i].positionZ);
                FloorMeshGroup._rotation.set(
                    degToRad(modelDatasWithUrl[i].heading),
                    degToRad(modelDatasWithUrl[i].pitch),
                    degToRad(modelDatasWithUrl[i].roll)
                );
                FloorMeshGroup._Quaternion.setFromEuler(FloorMeshGroup._rotation);

                // 只渲染当前选中的楼层模型 有当前高亮对象来渲染
                if (inFloorId) {
                    // 进入楼层时只显示进入的那层楼层的id
                    const scale = (modelDatasWithUrl[i].id === inFloorId) ? 1 : 0;
                    FloorMeshGroup._scale.set(scale, scale, scale);

                } else if (activeFloorId && activeFloorId === modelDatasWithUrl[i].id) {
                    //有选中楼层时就不显示当前楼层,  交给高亮模型去显示
                    const scale = (modelDatasWithUrl[i].id === activeFloorId) ? 0 : 1;
                    FloorMeshGroup._scale.set(scale, scale, scale);

                } else {
                    // 其他情况都显示楼层
                    FloorMeshGroup._scale.set(1, 1, 1);
                }
                FloorMeshGroup._Matrix4.compose(FloorMeshGroup._position, FloorMeshGroup._Quaternion, FloorMeshGroup._scale);
                instanceMeshGroup.setMatrixAt(i, FloorMeshGroup._Matrix4);

            }

        }

        // 创建点击模型盒的实例矩阵
        if (floorDatas.length !== this.modelEventBox.count) {
            this.modelEventBox.count = floorDatas.length;
            this.modelEventBox.instanceMatrix = new InstancedBufferAttribute(new Float32Array(floorDatas.length * 16), 16);
        }

        //设置点击模型盒的矩阵
        for (let i = 0; i < floorDatas.length; i++) {
            // 设置矩阵
            FloorMeshGroup._position.set(floorDatas[i].positionX, floorDatas[i].positionY, floorDatas[i].positionZ);
            const instanceMeshGroup = await this.getInstanceMeshGroupByUrl(floorDatas[i].url);

            if (addFloorid && (addFloorid !== floorDatas[i].id)) {
                FloorMeshGroup._scale.copy(instanceMeshGroup.size);
            } else {
                FloorMeshGroup._scale.set(0, 0, 0);
            }

            FloorMeshGroup._rotation.set(
                degToRad(floorDatas[i].heading),
                degToRad(floorDatas[i].pitch),
                degToRad(floorDatas[i].roll)
            );
            FloorMeshGroup._Quaternion.setFromEuler(FloorMeshGroup._rotation);
            FloorMeshGroup._Matrix4.compose(FloorMeshGroup._position, FloorMeshGroup._Quaternion, FloorMeshGroup._scale);
            this.modelEventBox.setMatrixAt(i, FloorMeshGroup._Matrix4);
        }
        this.modelEventBox.instanceMatrix.needsUpdate = true;
        this.modelEventBox.computeBoundingSphere();


    });

    //根据url 获取实例模型组  url
    async getInstanceMeshGroupByUrl(url: string): Promise<InstancedGroupMesh> {
        let instanceMeshGroup = this.group.children.find(o => o.url === url)!;
        if (!instanceMeshGroup) {

            const meshBase = new MeshBase(url);
            await new Promise<void>((resolve, reject) => {
                meshBase.addEventListener("loaded", () => {
                    meshBase.traverseMesh(mesh => {
                        const { color, map, transparent, opacity, alphaTest, side } = mesh.material as MeshBasicMaterial;
                        mesh.material = new MeshLambertMaterial({ color, map, transparent, opacity, alphaTest: 0.2, side });
                        if (!mesh.geometry.getAttribute("normal")) {
                            mesh.geometry.computeVertexNormals();
                        }
                        //     (mesh.material as MeshBasicMaterial).depthTest = true;
                        //     (mesh.material as MeshBasicMaterial).depthWrite = true;
                        //     (mesh.material as MeshBasicMaterial).transparent = false;
                        //     (mesh.material as MeshBasicMaterial).alphaTest = 0.2;
                    });
                    resolve();
                });
            });

            threeUtils.center(meshBase);
            threeUtils.alignBottom(meshBase);

            instanceMeshGroup = new InstancedGroupMesh(meshBase, 0);
            instanceMeshGroup.url = url;

            //当前的状态
            const { state } = editor3DStore;

            instanceMeshGroup.traverse(mesh => {

                mesh.userData = {
                    cursor: "pointer",
                    enableEvent: !["addFloor"].includes(state)
                };
                // 楼层点击事件
                mesh.addEventListener("leftclick", (e: any) => {

                    const { state, addModelUrl, addModelInfo, addLabelId, inFloorId } = editor3DStore;

                    if (state === "addModel" && addModelUrl) {
                        const modelDatas = toJS(editor3DStore.modelDatas);
                        editor3DStore.setModelDatas([
                            ...modelDatas,
                            {
                                id: generateUUID(),
                                name: addModelUrl,
                                url: addModelUrl,
                                floorId: inFloorId,
                                positionX: e.point.x,
                                positionY: e.point.y,
                                positionZ: e.point.z,
                                heading: 0,
                                pitch: 0,
                                roll: 0,
                                info: addModelInfo
                            }
                        ]);

                        // 添加标签数据
                    } else if (state === "addLabel" && addLabelId) {
                        editor3DStore.setLabelDataById(addLabelId, {
                            floorId: inFloorId,
                            positionX: e.point.x,  // x坐标
                            positionY: e.point.y,  // y坐标
                            positionZ: e.point.z,  // z坐标
                        })

                        // 添加监控
                    } else if (state === "addMonitor") {
                        editor3DStore.addMonitorData({
                            id: generateUUID(),
                            name: "",
                            floorId: inFloorId,
                            url: editor3DStore.monitorUrl,
                            positionX: e.point.x,  // x坐标
                            positionY: e.point.y,  // y坐标
                            positionZ: e.point.z,  // z坐标
                            heading: 0,
                            pitch: 0,
                            roll: 0,
                        });
                        //添加楼层
                    } else if (state === "addFloor") {


                    } else {
                        const floorDatas = toJS(editor3DStore.floorDatas);
                        const floorData = floorDatas.filter(d => d.url === instanceMeshGroup.url)[e.instanceId];
                        if (floorData) {
                            editor3DStore.setActiveFloorId(floorData.id);
                        }
                    }
                });
                // 双击是进入楼层
                mesh.addEventListener("dbclick", (e: any) => {
                    const floorDatas = toJS(editor3DStore.floorDatas);
                    const floorData = floorDatas.filter(d => d.url === instanceMeshGroup.url)[e.instanceId];
                    if (floorData) {
                        editor3DStore.setInFloorId(floorData.id);
                    }
                });
                mesh.addEventListener("rightclick", (e: any) => {
                    const { state, addModelUrl, addLabelId } = editor3DStore;
                    if (state === "addModel") {
                        editor3DStore.cancelAdd();
                    } else if (state === "addLabel" && addLabelId) {
                        editor3DStore.deleteLabelDate(addLabelId);
                    }
                    editor3DStore.cancelAdd();
                });

                // 移动添加预览模型
                mesh.addEventListener("mousemove", (e: any) => {
                    const { state, addModelUrl, addLabelId, addFloorid, activeFloorId } = editor3DStore;
                    if (state === "addModel") {
                        editor3D.addPreView.position.copy(e.point);
                    } else if (state === "addLabel" && addLabelId) {
                        editor3DStore.setLabelDataById(addLabelId, {
                            positionX: e.point.x,  // x坐标
                            positionY: e.point.y,  // y坐标
                            positionZ: e.point.z,  // z坐标
                        })
                    } else if (state === "addMonitor") {
                        editor3D.monitorGroup.addPervire.position.copy(e.point);
                    }
                });

            });

            if (!this.group.children.some(f => f.url === url)) {
                this.group.add(instanceMeshGroup);
            }
        }

        return instanceMeshGroup;
    }

    //
    dispose1 = autorun(() => {
        const { state, activeFloorId, inFloorId, floorDatas } = editor3DStore;

        if (["addModel", "addLabel", "addMonitor"].includes(state) && !inFloorId) {
            this.visible = false;
        } else {
            this.visible = true;
        }

        this.traverse(m => {
            m.userData = {
                cursor: "pointer",
                enableEvent: !["addFloor"].includes(state)
            };
        });

        // 添加模型时,事件盒子关闭鼠标事件
        Object.assign(this.modelEventBox.userData, { enableEvent: state === "addFloor" });
    });

}

const floorMeshGroup = new FloorMeshGroup();

export default floorMeshGroup;